﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProjectSchedule.Entity;
using System.Collections.Specialized;

namespace ProjectSchedule.Common
{
    public class FetalLayout
    {
        /// <summary>
        /// 胎位输入数据
        /// </summary>
        private TWSchedule _schedule;

        /// <summary>
        /// 胎位默认属性
        /// </summary>
        private IDictionary<String, Object[]> _defaultPropties;

        /// <summary>
        /// 胎位扩展信息
        /// </summary>
        private IDictionary<String, TWExtendInfo> _fetals;

        /// <summary>
        /// 分段布置列表
        /// </summary>
        private IList<TWSectionArrange> _sectionArranges;

        /// <summary>
        /// 所有分段
        /// </summary>
        private IList<TWSectionInfo> _allSections;

        /// <summary>
        /// 无效的分段
        /// </summary>
        private IList<TWSectionInfo> _invalidSections;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="schedule"></param>
        public FetalLayout(TWSchedule schedule)
        {
            _schedule = schedule;
            _allSections = _schedule.SectionInfos;
            _fetals = new Dictionary<String, TWExtendInfo>();
            _defaultPropties = new Dictionary<String, Object[]>();
            _sectionArranges = new List<TWSectionArrange>();
            _invalidSections = new List<TWSectionInfo>();

            InitDefaultPropties();
            InitFetals();
            StartArrange();
        }

        /// <summary>
        /// 初始化胎位默认属性
        /// </summary>
        private void InitDefaultPropties()
        {
            _defaultPropties.Clear();
            _defaultPropties.Add("A1",new Object[]{1,10,"A2",""});
            _defaultPropties.Add("A2", new Object[] { 2, 9, "A3,A19", "A1,A19" });
            _defaultPropties.Add("A3", new Object[] { 3, 8, "A4,A18", "A2,A18" });
            _defaultPropties.Add("A4", new Object[] { 4, 7, "A5,A17", "A3,A17" });
            _defaultPropties.Add("A5", new Object[] { 5, 6, "A6,A16", "A4,A16" });
            _defaultPropties.Add("A6", new Object[] { 6, 5, "A7,A15", "A5,A15" });
            _defaultPropties.Add("A7", new Object[] { 7, 4, "A8,A14", "A6,A14" });
            _defaultPropties.Add("A8", new Object[] { 8, 3, "A9,A13", "A7,A13" });
            _defaultPropties.Add("A9", new Object[] { 9, 2, "A10,A12", "A8,A12" });
            _defaultPropties.Add("A10", new Object[] { 10, 11, "A11", "A9,A11" });
            _defaultPropties.Add("A11", new Object[] { 10, 11, "A10", "A12,A10" });
            _defaultPropties.Add("A12", new Object[] { 9, 2, "A11,A9", "A13,A9" });
            _defaultPropties.Add("A13", new Object[] { 8, 3, "A12,A8", "A14,A8" });
            _defaultPropties.Add("A14", new Object[] { 7, 4, "A13,A7", "A15,A7" });
            _defaultPropties.Add("A15", new Object[] { 6, 5, "A14,A6", "A16,A6" });
            _defaultPropties.Add("A16", new Object[] { 5, 6, "A15,A5", "A17,A5" });
            _defaultPropties.Add("A17", new Object[] { 4, 7, "A16,A4", "A18,A4" });
            _defaultPropties.Add("A18", new Object[] { 3, 8, "A17,A3", "A19,A3" });
            _defaultPropties.Add("A19", new Object[] { 2, 9, "A18,A2", "A20,A2" });
            _defaultPropties.Add("A20", new Object[] { 1, 10, "A19", "" });

            _defaultPropties.Add("B1", new Object[] { 1, 10, "B2", "" });
            _defaultPropties.Add("B2", new Object[] { 2, 9, "B3,B19", "B1,B19" });
            _defaultPropties.Add("B3", new Object[] { 3, 8, "B4,B18", "B2,B18" });
            _defaultPropties.Add("B4", new Object[] { 4, 7, "B5,B17", "B3,B17" });
            _defaultPropties.Add("B5", new Object[] { 5, 6, "B6,B16", "B4,B16" });
            _defaultPropties.Add("B6", new Object[] { 6, 5, "B7,B15", "B5,B15" });
            _defaultPropties.Add("B7", new Object[] { 7, 4, "B8,B14", "B6,B14" });
            _defaultPropties.Add("B8", new Object[] { 8, 3, "B9,B13", "B7,B13" });
            _defaultPropties.Add("B9", new Object[] { 9, 2, "B10,B12", "B8,B12" });
            _defaultPropties.Add("B10", new Object[] { 10, 11, "B11", "B9,B11" });
            _defaultPropties.Add("B11", new Object[] { 10, 11, "B10", "B12,B10" });
            _defaultPropties.Add("B12", new Object[] { 9, 2, "B11,B9", "B13,B9" });
            _defaultPropties.Add("B13", new Object[] { 8, 3, "B12,B8", "B14,B8" });
            _defaultPropties.Add("B14", new Object[] { 7, 4, "B13,B7", "B15,B7" });
            _defaultPropties.Add("B15", new Object[] { 6, 5, "B14,B6", "B16,B6" });
            _defaultPropties.Add("B16", new Object[] { 5, 6, "B15,B5", "B17,B5" });
            _defaultPropties.Add("B17", new Object[] { 4, 7, "B16,B4", "B18,B4" });
            _defaultPropties.Add("B18", new Object[] { 3, 8, "B17,B3", "B19,B3" });
            _defaultPropties.Add("B19", new Object[] { 2, 9, "B18,B2", "B20,B2" });
            _defaultPropties.Add("B20", new Object[] { 1, 10, "B19", "" });
        }

        /// <summary>
        /// 初始化胎位信息
        /// </summary>
        private void InitFetals()
        {
            _fetals.Clear();
            TWExtendInfo extInfo;
            Int32 number;
            Object[] props;

            foreach (var b in _schedule.BasicInfos)
            {
                extInfo = new TWExtendInfo(b);
                extInfo.Group = extInfo.Position[0];

                number = Convert.ToInt32(extInfo.Position.Substring(1));
                props = _defaultPropties[extInfo.Position];

                if (props != null)
                {
                    extInfo.OrderNo = (Int32)props[0];
                    extInfo.Priority = (Int32)props[1];
                    extInfo.GetInPositions = String.IsNullOrEmpty((String)props[2]) 
                                           ? new String[0] 
                                           : ((String)props[2]).Split(',');
                    extInfo.GetOutPositions = String.IsNullOrEmpty((String)props[3])
                                            ? new String[0]
                                            : ((String)props[3]).Split(',');
                }
                else
                {
                    extInfo.OrderNo = -1;
                    extInfo.Priority = -1;
                    extInfo.GetInPositions = new String[0];
                    extInfo.GetOutPositions = new String[0];
                }

                _fetals.Add(extInfo.Position, extInfo);
            }
        }

        /// <summary>
        /// 胎位位置索引
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TWExtendInfo this[String key]
        {
             get
             {
                 return _fetals == null ? null : _fetals[key];
             }
        }

        /// <summary>
        /// 开始布置分段
        /// </summary>
        private void StartArrange()
        {
            Single maxHeight=_fetals.Values.Max(m=>m.HangingHeight-m.Height);
            Single maxWidth=_fetals.Values.Max(m=>m.Width);
            IList<TWSectionInfo> allSections = _allSections.OrderBy(m => m.UpFetalDate)
                                                           .ThenBy(m => m.DownFetalDate)
                                                           .ToList();

            Boolean flag;
            String[] fetals;
            TWExtendInfo _fetal;
            String[] positions;
            List<TWSectionArrange> currentArrange;

            foreach (var section in allSections)
            {
                flag = false;

                if (maxHeight < section.Height || maxWidth < section.Width)
                {
                    section.InvalidMessage = "分段过大";
                }
                else
                {
                    positions = GetAvailableFetal(section.UpFetalDate, section);

                    foreach (var position in positions)
                    {
                        fetals = position.Split(',');
                        currentArrange = new List<TWSectionArrange>();
                        currentArrange.AddRange(_sectionArranges);
                        currentArrange.AddRange(fetals.Select(m => new TWSectionArrange(section, m)));

                        if (VerifySectionOut(section.UpFetalDate, section.DownFetalDate, currentArrange))
                        {
                            foreach (var f in fetals)
                            {
                                _fetal = _fetals[f];

                                for (var d = section.UpFetalDate; d <= section.DownFetalDate; d = d.AddDays(1))
                                {
                                    _fetal.SectionOfDate[d.ToString("yyyyMMdd")] = section.ShipName + "_" + section.SectionName;
                                }
                            }

                            _sectionArranges.Add(new TWSectionArrange(section, position));

                            flag = true;

                            break;
                        }
                    }
                }

                if (!flag)
                {
                    _invalidSections.Add(section);
                }
            }
        }

        /// <summary>
        /// 获取胎位线路
        /// </summary>
        /// <returns></returns>
        public List<List<String>> GetFetalLine(String position, String filterPosition=null)
        {
            var fetal = _fetals[position];
            if (fetal == null)
            {
                return new List<List<String>>();
            }
            else if (fetal.GetOutPositions.Length == 0) 
            {
                return new List<List<String>> { new List<String> { position } };
            }
            else
            {
                List<List<String>> line = GetFetalLine(fetal.GetOutPositions[0]);

                if (fetal.GetOutPositions[1] != filterPosition)
                {
                    line.AddRange(GetFetalLine(fetal.GetOutPositions[1], position));
                }

                foreach (var l in line)
                {
                    l.Insert(0, position);
                }

                return line;
            }
        }

        /// <summary>
        /// 获取某天空闲的胎位
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private String[] GetFreeFetal(DateTime date)
        {
            List<TWExtendInfo> freeFetals = new List<TWExtendInfo>();

            foreach (var fetal in _fetals.Values)
            { 
                if(!fetal.SectionOfDate.ContainsKey(date.ToString("yyyyMMdd")))
                {
                    freeFetals.Add(fetal);
                }
            }

            return freeFetals.OrderBy(m => m.Priority)
                             .Select(m => m.Position)
                             .ToArray();
        }

        /// <summary>
        /// 获取空闲胎位组合
        /// </summary>
        /// <param name="freeFetals"></param>
        /// <returns></returns>
        private String[] GetFreeCombinFetal(String[] positions)
        {
            IList<String> result = new List<String>(); ;
            TWExtendInfo fetal;

            foreach (var position in positions)
            { 
                fetal=_fetals[position];

                if (fetal.GetOutPositions.Length>0 && 
                    positions.Contains(fetal.GetOutPositions[0]))
                {
                    result.Add(position + "," + fetal.GetOutPositions[0]);
                }
            }

            return result.ToArray();
        }

        /// <summary>
        /// 获取某天可用的胎位
        /// </summary>
        /// <param name="date"></param>
        /// <param name="twsection"></param>
        /// <returns></returns>
        private String[] GetAvailableFetal(DateTime date, TWSectionInfo twsection)
        {
            List<String> usableFetals = new List<String>();
            String[] freeFetals = GetFreeFetal(date);
            String[] freeCombinFetals = GetFreeCombinFetal(freeFetals);
            List<List<String>> lines;
            Boolean flag = false;

            TWExtendInfo twextend;
            foreach (var fetal in freeFetals)
            {
                twextend = _fetals[fetal];
                lines = GetFetalLine(fetal,null);

                if (twsection.Width <= twextend.Width &&
                    twsection.Long <= twextend.Long &&
                    lines.Any(l => VerifySectionLine(date, l, twsection)))
                {
                    usableFetals.Add(fetal);
                    flag = true;
                }
            }

            if (!flag)
            {
                TWExtendInfo twextend1;
                TWExtendInfo twextend2;
                String[] fetals;
                foreach (var fetal in freeCombinFetals)
                {
                    fetals = fetal.Split(',');
                    twextend1 = _fetals[fetals[0]];
                    twextend2 = _fetals[fetals[1]];
                    lines = GetFetalLine(fetals[0], null);

                    if (twsection.Width <= twextend1.Width &&
                        twsection.Long <= twextend1.Long + twextend2.Long &&
                        lines.Any(l => VerifySectionLine(date, l, twsection)))
                    {
                        usableFetals.Add(fetal);
                    }
                }
            }

            return usableFetals.ToArray();
        }

        /// <summary>
        /// 验证分段吊出情况
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <param name="sections"></param>
        /// <returns></returns>
        private Boolean VerifySectionOut(DateTime startDate, DateTime endDate, IList<TWSectionArrange> sections)
        {
            Boolean result = true;
            List<List<String>> lines;
            var getoutSections = _sectionArranges.Where(m => m.DownFetalDate > startDate && m.DownFetalDate < endDate)
                                                 .ToList();

            foreach (var section in getoutSections)
            {
                lines = GetFetalLine(section.Position.Split(',')[0]);

                if (lines.All(l => !VerifySectionLine(section.DownFetalDate, l, section)))
                {
                    result = false;
                }
            }

            return result;
        }

        /// <summary>
        /// 验证分段线路
        /// </summary>
        /// <param name="date"></param>
        /// <param name="line"></param>
        /// <param name="section"></param>
        /// <returns></returns>
        private Boolean VerifySectionLine(DateTime date, List<String> line, TWSectionInfo section)
        {
            TWExtendInfo fetal = null;
            Single existSectionHeight = 0;

            foreach (var position in line)
            {
                fetal = _fetals[position];
                if (fetal.SectionOfDate.ContainsKey(date.ToString("yyyyMM")))
                {
                    existSectionHeight = _allSections.Where(m => m.ShipName + "_" + m.SectionName == fetal.SectionOfDate[date.ToString("yyyyMM")])
                                                     .Select(m => m.Height)
                                                     .FirstOrDefault();
                }
                else
                {
                    existSectionHeight = 0;
                }

                if (fetal.Height + existSectionHeight + section.Height > fetal.HangingHeight)
                {
                    return false;
                }
            }
            return true;
        }

        #region 输出

        /// <summary>
        /// 获取所有胎位分段流转
        /// </summary>
        /// <returns></returns>
        public IList<TWSectionArrange> GetAllSectionArrange()
        {
            return _sectionArranges;
        }

        /// <summary>
        /// 获取某月胎位分段流转
        /// </summary>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <returns></returns>
        public IList<TWSectionArrange> GetSectionArrangeByMonth(Int32 year, Int32 month)
        {
            DateTime startDate=new DateTime(year,month,1);
            DateTime endDate = startDate.AddMonths(1).AddDays(-1);

            return _sectionArranges.Where(m => m.UpFetalDate <= endDate && m.DownFetalDate >= startDate).ToList();
        }

        /// <summary>
        /// 获取不可用的分段
        /// </summary>
        /// <returns></returns>
        public IList<TWSectionInfo> GetInvalidSections()
        {
            return _invalidSections;
        }

        #endregion
    }
}
